package org.leumasjaffe.initiativeTracker.entity;


import java.io.IOException;
import java.io.PrintStream;
import java.io.Serializable;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.leumasjaffe.initiativeTracker.Constants;

import org.leumasjaffe.util.io.SerializeString;

/**
 * An abstract of a character containing all relevant information
 * @author Sam Jaffe
 *
 */
@NonNullByDefault
public abstract class Character implements IStatus, Comparable<Character>, Serializable{
	private static final long serialVersionUID = -2179350930935337254L;

	private boolean delayed = false;

	private String name;
	private int hp;
	private int maxHP;
	private int ac;
	private int fort;
	private int ref;
	private int will;
	private int init;
	private int id = -1;
	private int dot = 0;

	private int dis = 0;
		
	private boolean[] status = new boolean[15];
		
	/**
	 * Creates an empty character for use
	 */
	Character() {
	}

	/**
	 * The standard constructor for Characters
	 * @param id The id number of the character, used for super-efficient comparison
	 */
	Character(int id) {
		this.setId(id);
	}

	
	public void updateStatus(int index, boolean bool) {
		this.status[index] = bool;
	}
	
	/**
	 * Compares this with a given object
	 * @param o The Object we are comparing to
	 * @return A boolean indicating whether or not this and o are equal
	 */
	public boolean equal(Object o){
		if (o instanceof Character)
			return this.same((Character) o);
		else
			return false;
	}

	/**
	 * Compares this Character with a given Character
	 * @param c The Character we are comparing to
	 * @return Whether the id's are the same and non-negative
	 */
	private boolean same(Character c) {
		return this.getId() == c.getId() && this.getId() != -1;
	}

	/**
	 * Exports the character's data to the file in the PrintStream
	 * @param out The PrintStream to the file we are writing to
	 */
	public void export(PrintStream out) {
		out.println(this.name);
		out.println("NUMBER VALUES:: " + this.hp + "," + this.maxHP + "," + this.ac +
				"," + this.fort + "," + this.ref + "," + this.will +
				"," + this.init + "," + this.id + "," + this.dot + "," + this.dis);
		out.println("BOOLEAN VALUES:: " + statusAsBitString());
	}

	public void setName(String name) {
		this.name = name;
	}

	@SuppressWarnings("null")
	public String getName() {
		return this.name;
	}

	public void setHp(int hp) {
		this.hp = hp;
	}

	public int getHp() {
		return this.hp;
	}

	public void setMaxHP(int maxHP) {
		this.maxHP = maxHP;
	}

	public int getMaxHP() {
		return this.maxHP;
	}

	public void setAc(int ac) {
		this.ac = ac;
	}

	public int getAc() {
		return this.ac;
	}

	public void setFort(int fort) {
		this.fort = fort;
	}

	public int getFort() {
		return this.fort;
	}

	public void setRef(int ref) {
		this.ref = ref;
	}

	public int getRef() {
		return this.ref;
	}

	public void setWill(int will) {
		this.will = will;
	}

	public int getWill() {
		return this.will;
	}

	public void setInit(int init) {
		this.init = init;
	}

	public int getInit() {
		return this.init;
	}

	public void nullInit() {
		this.init = 0;
	}

	public void setId(int id) {
		this.id = id;
	}

	public int getId() {
		return this.id;
	}

	public void setDot(int dot) {
		this.dot = dot;
	}

	public int getDot() {
		return this.dot;
	}

	public void setDis(int dis) {
		this.dis = dis;
	}

	public int getDis() {
		return this.dis;
	}

	public void checkDelayed() {
		this.delayed = true;
	}

	public boolean isDelayed() {
		return this.delayed;
	}

	public boolean notObs() {
		return this instanceof PC || Constants.isDM;
	}
	
	@SuppressWarnings("null")
	public boolean[] getStatus() {
		return this.status;
	}
	
	@Override
	public int statusAsBitString() {
		int temp = 0;
		for (int i = 0; i < this.status.length; i++)
			if (this.status[i]) temp += 1 << i;
		return temp;
	}

	@Override
	public void readStatus(int bits) {
		for (int i = 0; i < this.status.length; i++)
			if ((1 & bits >> i) == 1) this.status[i] = true;
	}

//	public String toXML() {
//		String xmlString = "<character name=\"" + this.name + "\" id='" + getId() + "' type='" +
//			(this instanceof NPC ? (((NPC) this).isAllied() ? "friend" : "foe") : "player") + 
//			"' health='" + hpCode() + "' base='" + baseCode() + "' defense='" + defenseCode() 
//			+ "' status='" + statusAsBitString() + "'" +
//			(this instanceof PC ? " player='" + ((PC) this).playerCode() + "'" : "") + 
//			"/>";
//		return xmlString;
//	}
	
	public String serialize() {
		String serialized = "";
		try {
			serialized = SerializeString.toString(this);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return "<character_ser type='" + (this instanceof NPC ? (((NPC) this).isAllied() ?
				"friend" : "foe") : "player") + "'><serial>" + serialized + "</serial></character_ser>";
	}

//	private int hpCode() {
//		return (this.maxHP << 16) + this.hp;
//	}
//	
//	private int baseCode() {
//		int code = (((byte)this.init << 8)) + (((byte)this.dis << 24)) + ((byte)this.dot);
//		if (this instanceof NPC)
//			code += ((byte)((NPC) this).getCount() << 16);
//		return code;
//	}
//	
//	private int defenseCode() {
//		return (((byte)this.ac << 24)) + (((byte)this.fort << 16))
//				+ (((byte)this.ref << 8)) + (((byte)this.will));
//	}
	
	@Override
	public int compareTo(@Nullable Character arg0) {
		if (arg0 == null) return -1;
		int ret = arg0.getInit() - this.init;
		return ret == 0 ? getName().compareTo(arg0.getName()) : ret;
	}
}
